#include "udp/udp_server.hpp"

#define logi(_fmt, ...) \
    printf("udp[I] ");  \
    printf(_fmt "\n", ##__VA_ARGS__)
#define loge(_fmt, ...) \
    printf("udp[E] ");  \
    printf(_fmt "\n", ##__VA_ARGS__)

// 使用完记得释放指针     freeaddrinfo(addr_info);
struct addrinfo *get_udp_addrinfo(const std::string &ip, int cliceport)
{
    struct addrinfo hints;
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_UNSPEC;    // 允许IPv4或IPv6
    hints.ai_socktype = SOCK_DGRAM; // UDP
    hints.ai_protocol = IPPROTO_UDP;

    std::string port_str = "";
    std::string ip_str = "::";
    if (cliceport > 0)
    {
        port_str = std::to_string(cliceport).c_str();
    }

    if (ip != "")
    {
        ip_str = ip;
    }

    struct addrinfo *res = nullptr;
    int ret = getaddrinfo(ip_str.c_str(), port_str.c_str(), &hints, &res);
    if (ret != 0)
    {
        loge("ip=[%s] port=[%d] getaddrinfo error! rs=%d", ip_str.c_str(), cliceport, ret);
        return nullptr; // 错误时返回空指针
    }
    // logi("ip=[%s] port=[%d] getaddrinfo ok! rs=%d", ip_str.c_str(), cliceport, ret);
    return res; // 成功时返回 addrinfo 指针
}

int ADEALL_UDP_SERVER::CreateIpFd(std::string iface, int cliceport)
{

    addrinfo *addr = get_udp_addrinfo(iface, cliceport);
    if (addr != nullptr)
    {

        // 创建套接字
        int srv_fd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
        if (srv_fd < 0)
        {
            loge("udp6 %d socket error!", cliceport);
            return -1;
        }

        if (bind(srv_fd, addr->ai_addr, addr->ai_addrlen) < 0)
        {
            loge("udp %d bink error!", cliceport);
#ifdef _WIN32
            closesocket(srv_fd);
#else
            close(srv_fd);
#endif
            freeaddrinfo(addr);
            return -1;
        }
        freeaddrinfo(addr);

        logi("create_Ip_fd %s ==> %d ok! fd=%d", iface.c_str(), cliceport, srv_fd);
        return srv_fd;
    }

    return -1;
}

int ADEALL_UDP_SERVER::UdpSend(const char *ip, int port, const char *data, int L, int send_fd)
{

    struct addrinfo *res = get_udp_addrinfo(ip, port);
    if (res == nullptr)
        return -1;

    int rs = sendto(send_fd, data, L, 0, res->ai_addr, res->ai_addrlen);
    freeaddrinfo(res);
    if (rs < 0)
    {
        logi("udp ip=%s port=%d send error! rs=%d", ip, port, rs);
        // perror("sendto");
    }
    else
    {
        // logi("udp ip6=%s port=%d send ok ! %d\n", ip, port, send_fd);
        return rs;
    }

    return -1;
}

#ifdef _WIN32
static boolean WSAStartup_flag = false;
#endif

ADEALL_UDP_SERVER::ADEALL_UDP_SERVER(/* args */)
{
    _callback = nullptr;
    _context = nullptr;
    srv_fd = -1;
    recv_runing_N = 0;
    buff_size = 1024;
    thread_max = 100;

#ifdef _WIN32
    if (WSAStartup_flag == false)
    {
        system("chcp 65001"); // 使Cmd 中文不乱码 65001 代表 UTF-8代码页
        WSADATA wsaData;
        if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
        {
            std::cerr << "WSAStartup failed." << std::endl;
            exit(-1);
        }
        WSAStartup_flag = true;
    }
#endif
}

ADEALL_UDP_SERVER::~ADEALL_UDP_SERVER()
{
#ifdef _WIN32
    closesocket(srv_fd);
    if (WSAStartup_flag == true)
    {
        WSACleanup();
        WSAStartup_flag = false;
    }

#else
    close(srv_fd);
#endif
}
// 性能打印
void *ADEALL_UDP_SERVER::PrintfInfo(void *prt)
{
    ADEALL_UDP_SERVER *p = (ADEALL_UDP_SERVER *)prt;
    while (1)
    {
        printf("r=%d max=%d   %ld\n", p->recv_runing_N, p->thread_max, p->messages_queue.size());
        sleep_ms(100);
    }

    return nullptr;
}
void *ADEALL_UDP_SERVER::Msgthread(void *prt)
{
    ADEALL_UDP_SERVER *p = (ADEALL_UDP_SERVER *)prt;
    p->recv_runing_N++;
    // printf("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n");
    p->processMessages();
    p->recv_runing_N--;
    // printf("free thread*************\n");
    return nullptr;
}
void ADEALL_UDP_SERVER::processMessages()
{
    while (1)
    {
        msg_lock.lock();
        if (!messages_queue.empty())
        {
            UDP_MSG msg = messages_queue.front();
            messages_queue.pop();
            msg_lock.unlock();
            if (_callback == nullptr)
            {
                CallBackFuntion(msg.cli_ip,
                                msg.cli_port,
                                msg.buffer,
                                _context);
            }
            else
            {
                (*_callback)(msg.cli_ip,
                             msg.cli_port,
                             msg.buffer,
                             _context);
            }
        }
        else
        {
            msg_lock.unlock();
            for (size_t i = 0; i < 1000; i++)
            {
                if (!messages_queue.empty())
                {
                    break;
                }
                sleep_ms(1);
            }
            if (messages_queue.empty())
                return;
        }
    }
}

int get_addrinfo(const struct sockaddr *addr, std::string &ip, in_port_t &port)
{
    void *numeric_addr = NULL;
    char addr_buff[INET6_ADDRSTRLEN];

    if (AF_INET == addr->sa_family)
    {
        numeric_addr = &((struct sockaddr_in *)addr)->sin_addr;
        port = ntohs(((struct sockaddr_in *)addr)->sin_port);
    }
    else if (AF_INET6 == addr->sa_family)
    {
        numeric_addr = &((struct sockaddr_in6 *)addr)->sin6_addr;
        port = ntohs(((struct sockaddr_in6 *)addr)->sin6_port);
    }
    else
    {
        return -1;
    }

    if (NULL != inet_ntop(addr->sa_family, numeric_addr, addr_buff, sizeof(addr_buff)))
        ip = addr_buff;
    else
        return -1;

    return 0;
}
void *ADEALL_UDP_SERVER::UDPrevthreadfunAll(void *ptr)
{
    ADEALL_UDP_SERVER *p = (ADEALL_UDP_SERVER *)ptr;
    struct sockaddr_storage cliaddr;
    socklen_t len = sizeof(cliaddr);

    std::vector<uint8_t> recv_buff(p->buff_size);
    int recvL = 0;

    while (1)
    {
        recvL = recvfrom(p->srv_fd, (char *)&(recv_buff[0]), p->buff_size, 0, (struct sockaddr *)&cliaddr, &len);
        if (recvL <= 0)
        {
            continue;
        }
        else
        {
            UDP_MSG msg;
            for (size_t i = 0; i < recvL; i++)
            {
                msg.buffer.push_back(recv_buff[i]);
            }
            msg.len = recvL;
            get_addrinfo((struct sockaddr *)&cliaddr, msg.cli_ip, msg.cli_port);
            p->msg_lock.lock();
            p->messages_queue.push(msg);
            p->msg_lock.unlock();

            if (p->recv_runing_N < 1)
                ThreadBase(Msgthread, p);
            int runsize = p->messages_queue.size();
            if (runsize > 100 * p->recv_runing_N && p->recv_runing_N < p->thread_max)
                ThreadBase(Msgthread, p);
        }
    }
    return 0;
}

int ADEALL_UDP_SERVER::Init(int cliceport, void *prt, std::string iface, size_t N, FunCallBackFuntion *fun)
{

    if (N > 1)
        thread_max = N;

    _context = prt;
    _callback = fun;

    srv_fd = CreateIpFd(iface, cliceport);
    if (srv_fd < 0)
    {
        exit(-1);
        return -1;
    }
    ThreadBase(UDPrevthreadfunAll, this);
    // ThreadBase(UDPrevthreadfunAll, this);
    // ThreadBase(PrintfInfo, this);

    logi("创建成功");
    return 0;
}

void ADEALL_UDP_SERVER::MessagSend(const char *ip, int port, const char *data, int L, int fd)
{
    ScopeLocker lock(&sendcs);
    int send_fd;
    // printf("发送fd=%d\n", fd);
    if (fd > 0)
    {
        send_fd = fd;
    }
    else
    {
        // printf("服务端发送\n");
        send_fd = srv_fd;
    }
    UdpSend(ip, port, data, L, send_fd);
}

void ADEALL_UDP_SERVER::MessagSend(const char *ip, int port, const std::string data, int fd)
{
    MessagSend(ip, port, data.c_str(), data.length(), fd);
}
void ADEALL_UDP_SERVER::CallBackFuntion(std::string ip, int port, std::vector<uint8_t> x, void *)
{
}

// 加入组播监听 组播地址 绑定网卡地址
int ADEALL_UDP_SERVER::AddMulticast(std::string Multicast_ip, std::string iface_ip)
{

    struct ip_mreq mreq;
    // char group_ip[20];

    // strcpy(group_ip, Multicast_ip.c_str());
    mreq.imr_multiaddr.s_addr = inet_addr(Multicast_ip.c_str());
    mreq.imr_interface.s_addr = inet_addr(iface_ip.c_str());
    // 加入组播接收
    if (setsockopt(srv_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char *)&mreq, sizeof(mreq)) < 0)
    {
        perror("setsockopt multicast!");
        return -1;
    }

    logi("add multicast %s ipv4 ok!%s", Multicast_ip.c_str(), iface_ip.c_str());

    return 0;
}
void ADEALL_UDP_SERVER::SetRecvBuffSize(size_t s)
{
    buff_size = s;
}
size_t ADEALL_UDP_SERVER::get_running_N()
{
    return recv_runing_N;
}
size_t ADEALL_UDP_SERVER::get_msg_N()
{
    return messages_queue.size();
}